﻿namespace IBTrader.Modules.Tws
{
    using IBApi;
    using IBTrader.Modules.External;
    using NLog;
    using System;
    using System.Reflection;

    class IBClient : EClientSocket, EWrapper, IIB
    {
        private static readonly Logger logger = LogManager.GetCurrentClassLogger();
        public int ClientId { get; set; }
        public string AccountName { get; set; }
        public string Host { get; set; }
        public int Port { get; set; }

        public IBClient() : base(null)
        {
            typeof(EClientSocket).GetField("wrapper", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(this, this);
        }
        public IBClient(string host, string port, string clientId, string accountName) : this()
        {
            Init(host, port, clientId, accountName);
        }
        public void Init(string host, string port, string clientId, string accountName = null)
        {
            this.Host = host;
            this.Port = int.Parse(port);
            this.ClientId = int.Parse(clientId);
            this.AccountName = accountName;
        }

        public event EventHandler Connected;
        public void Connect()
        {
            eConnect(Host, Port, ClientId);
            if (Connected != null) Connected(this, EventArgs.Empty);
        }

        public void Disconnect()
        {
            eDisconnect();
        }

        public object CreateContract(string symbol, string securityType, string exchange, string currency, string multiplier, string primaryExchange, DateTime? dt = null)
        { 
            var contract = new Contract { Symbol = symbol, SecType = securityType, Exchange = exchange, Currency = currency, Multiplier = multiplier, PrimaryExch = primaryExchange };
            if (securityType == "FUT")
            {
                contract.Expiry = IBTrader.Modules.Market.Expirations.Expiration(symbol, dt ?? DateTime.UtcNow);
                contract.IncludeExpired = true;
            }
            return contract;
        }

        public void reqHistoricalData(int tickerId, object contract, string endDateTime, string durationString, string barSizeSetting, string whatToShow, int useRTH, int formatDate)
        {
            base.reqHistoricalData(tickerId, contract as Contract, endDateTime, durationString, barSizeSetting, whatToShow, useRTH, formatDate);
        }

        public void reqAccountUpdates(int subscribe, string accountCode)
        {
            base.reqAccountUpdates(subscribe == 1, accountCode);
        }

        public void reqContractDetails(int reqId, object contract)
        {
            base.reqContractDetails(reqId, contract as Contract);
        }

        public void reqMktData(int tickerId, object contract, string genericTickList, bool snapshot)
        {
            base.reqMktData(tickerId, contract as Contract, genericTickList, snapshot);
        }

        public void reqRealTimeBars(int tickerId, object contract, int barSize, string whatToShow, bool useRTH)
        {
            base.reqRealTimeBars(tickerId, contract as Contract, barSize, whatToShow, useRTH);
        }

        public void placeOrder(int id, object contract, object order)
        {
            base.placeOrder(id, contract as Contract, order as Order);
        }

        public void reqExecutions(int reqId, object filter)
        {
            base.reqExecutions(reqId, filter as ExecutionFilter);
        }

        public void reqMktDepth(int tickerId, object contract, int numRows)
        {
            base.reqMarketDepth(tickerId, contract as Contract, numRows);
        }

        public void reqFundamentalData(int reqId, object contract, string reportType)
        {
            base.reqFundamentalData(reqId, contract as Contract, reportType);
        }

        public object CreateExecutionFilter()
        {
            return new ExecutionFilter();
        }

        public object CreateOrder()
        {
            return new Order();
        }
        public object CreateOrder(OrderAction action, OrderType orderType, double lmtPrice, int quantity, string orderRef, TimeSpan orderDuration)
        {
            var order = new Order(); 
            order.Action = action.ToString();
            order.OrderType = orderType == OrderType.MKTPROT ? "MKT PRT" : orderType.ToString(); // LMT, MKT, MKT PRT, STP, REL, TRAIL
            if (orderType == OrderType.LMT || orderType == OrderType.REL) order.LmtPrice = lmtPrice;
            order.TotalQuantity = quantity;
            order.OrderRef = orderRef;
            order.Tif = "GTD"; // DAY, GTD, OPG, IOC
            if (order.Tif == "GTD") order.GoodTillDate = (Time.EasternNow + Time.Difference + orderDuration).ToString("yyyyMMdd HH:mm:ss EST");
            order.ClientId = ClientId;
            order.Account = AccountName;
            return order;
        }

        public event AxTWSLib._DTwsEvents_errMsgEventHandler errMsg;
        public void error(int id, int errorCode, string errorMsg)
        {
            if (errMsg != null) errMsg(this, new AxTWSLib._DTwsEvents_errMsgEvent ( id, errorCode, errorMsg ));
        }
        public void error(string errorMsg)
        {
            if (errMsg != null) errMsg(this, new AxTWSLib._DTwsEvents_errMsgEvent(-1, -1, errorMsg));
        }
        public void error(Exception e)
        {
            if (errMsg != null) errMsg(this, new AxTWSLib._DTwsEvents_errMsgEvent(-1, -1, e.ToString()));
        }

        public event EventHandler connectionClosed;
        void EWrapper.connectionClosed()
        {
            if (connectionClosed != null) connectionClosed(this, EventArgs.Empty);
        }

        public event AxTWSLib._DTwsEvents_historicalDataEventHandler historicalData;
        void EWrapper.historicalData(int reqId, string date, double open, double high, double low, double close, int volume, int count, double WAP, bool hasGaps)
        {
            if (historicalData != null) historicalData(this, new AxTWSLib._DTwsEvents_historicalDataEvent(reqId, date, open, high, low, close, volume, count, WAP, hasGaps ? 1 : 0));
        }

        public event AxTWSLib._DTwsEvents_historicalDataEventHandler historicalDataEnd;
        void EWrapper.historicalDataEnd(int reqId, string start, string end)
        {
            if (historicalDataEnd != null) historicalDataEnd(this, new AxTWSLib._DTwsEvents_historicalDataEvent(reqId, start, 0, 0, 0, 0, 0, 0, 0, 0));
        }

        public event AxTWSLib._DTwsEvents_updateAccountValueEventHandler updateAccountValue;
        void EWrapper.updateAccountValue(string key, string value, string currency, string accountName)
        {
            if (updateAccountValue != null) updateAccountValue(this, new AxTWSLib._DTwsEvents_updateAccountValueEvent(key, value, currency, accountName));
        }

        public event AxTWSLib._DTwsEvents_tickPriceEventHandler tickPrice;
        void EWrapper.tickPrice(int tickerId, int field, double price, int canAutoExecute)
        {
            if (tickPrice != null) tickPrice(this, new AxTWSLib._DTwsEvents_tickPriceEvent(tickerId, field, price, canAutoExecute));
        }

        public event AxTWSLib._DTwsEvents_tickSizeEventHandler tickSize;
        void EWrapper.tickSize(int tickerId, int field, int size)
        {
            if (tickSize != null) tickSize(this, new AxTWSLib._DTwsEvents_tickSizeEvent(tickerId, field, size));
        }

        public event EventHandler<ContractDetailsEventArgs> contractDetails;
        void EWrapper.contractDetails(int reqId, ContractDetails _contractDetails)
        {
            if (contractDetails != null) contractDetails(this, new ContractDetailsEventArgs { RequestId = reqId, ContractDetails = _contractDetails });
        }

        public event AxTWSLib._DTwsEvents_contractDetailsEndEventHandler contractDetailsEnd;
        void EWrapper.contractDetailsEnd(int reqId)
        {
            if (contractDetailsEnd != null) contractDetailsEnd(this, new AxTWSLib._DTwsEvents_contractDetailsEndEvent(reqId));
        }

        public event AxTWSLib._DTwsEvents_updateAccountTimeEventHandler updateAccountTime;
        void EWrapper.updateAccountTime(string timestamp)
        {
            if (updateAccountTime != null) updateAccountTime(this, new AxTWSLib._DTwsEvents_updateAccountTimeEvent(timestamp));
        }

        public event AxTWSLib._DTwsEvents_accountDownloadEndEventHandler accountDownloadEnd;
        void EWrapper.accountDownloadEnd(string account)
        {
            if (accountDownloadEnd != null) accountDownloadEnd(this, new AxTWSLib._DTwsEvents_accountDownloadEndEvent(account));
        }

        public event AxTWSLib._DTwsEvents_tickStringEventHandler tickString;
        void EWrapper.tickString(int tickerId, int tickType, string value)
        {
            var type = TickType.getField(tickType);
            if (tickString != null) tickString(this, new AxTWSLib._DTwsEvents_tickStringEvent(tickerId, tickType, value));
        }

        public event AxTWSLib._DTwsEvents_nextValidIdEventHandler nextValidId;
        void EWrapper.nextValidId(int orderId)
        {
            if (nextValidId != null) nextValidId(this, new AxTWSLib._DTwsEvents_nextValidIdEvent(orderId));
        }

        public event AxTWSLib._DTwsEvents_managedAccountsEventHandler managedAccounts;
        void EWrapper.managedAccounts(string accountsList)
        {
            if (managedAccounts != null) managedAccounts(this, new AxTWSLib._DTwsEvents_managedAccountsEvent(accountsList));
        }

        public event AxTWSLib._DTwsEvents_tickGenericEventHandler tickGeneric;
        void EWrapper.tickGeneric(int tickerId, int tickType, double value)
        {
            if (tickGeneric != null) tickGeneric(this, new AxTWSLib._DTwsEvents_tickGenericEvent(tickerId, tickType, value));
        }

        public event EventHandler<UpdatePortfolioEventArgs> updatePortfolio;
        void EWrapper.updatePortfolio(Contract contract, int position, double marketPrice, double marketValue, double averageCost, double unrealisedPNL, double realisedPNL, string accountName)
        {
            if (updatePortfolio != null) updatePortfolio(this, new UpdatePortfolioEventArgs { contract = contract, position= position, marketPrice = marketPrice, 
                marketValue= marketValue, averageCost= averageCost, unrealizedPNL= unrealisedPNL,realizedPNL= realisedPNL, accountName= accountName});
        }

        public event EventHandler<OpenOrderEventArgs> openOrder;
        void EWrapper.openOrder(int orderId, Contract contract, Order order, OrderState orderState)
        {
            if (openOrder != null) openOrder(this, new OpenOrderEventArgs { orderId = orderId, contract = contract, order = order, orderState = orderState });
        }

        public event EventHandler openOrderEnd;
        void EWrapper.openOrderEnd()
        {
            if (openOrderEnd != null) openOrderEnd(this, EventArgs.Empty);
        }

        public event EventHandler<ExecDetailsEventArgs> execDetails;
        void EWrapper.execDetails(int reqId, Contract contract, Execution execution)
        {
            if (execDetails != null) execDetails(this, new ExecDetailsEventArgs { reqId = reqId, contract = contract, execution = execution });
        }

        public event AxTWSLib._DTwsEvents_execDetailsEndEventHandler execDetailsEnd;
        void EWrapper.execDetailsEnd(int reqId)
        {
            if (execDetailsEnd != null) execDetailsEnd(this, new AxTWSLib._DTwsEvents_execDetailsEndEvent(reqId));
        }

        public event AxTWSLib._DTwsEvents_updateMktDepthEventHandler updateMktDepth;
        void EWrapper.updateMktDepth(int tickerId, int position, int operation, int side, double price, int size)
        {
            if (updateMktDepth != null) updateMktDepth(this, new AxTWSLib._DTwsEvents_updateMktDepthEvent(tickerId, position, operation, side, price, size));
        }

        public event AxTWSLib._DTwsEvents_updateMktDepthL2EventHandler updateMktDepthL2;
        void EWrapper.updateMktDepthL2(int tickerId, int position, string marketMaker, int operation, int side, double price, int size)
        {
            if (updateMktDepthL2 != null) updateMktDepthL2(this, new AxTWSLib._DTwsEvents_updateMktDepthL2Event(tickerId, position, marketMaker, operation, side, price, size));
        }

        public event AxTWSLib._DTwsEvents_updateNewsBulletinEventHandler updateNewsBulletin;
        void EWrapper.updateNewsBulletin(int msgId, int msgType, string message, string origExchange)
        {
            if (updateNewsBulletin != null) updateNewsBulletin(this, new AxTWSLib._DTwsEvents_updateNewsBulletinEvent((short)msgId, (short)msgType, message, origExchange));
        }

        public event AxTWSLib._DTwsEvents_realtimeBarEventHandler realtimeBar;
        void EWrapper.realtimeBar(int reqId, long time, double open, double high, double low, double close, long volume, double WAP, int count)
        {
            if (realtimeBar != null) realtimeBar(this, new AxTWSLib._DTwsEvents_realtimeBarEvent(reqId, (int)time, open, high, low, close, (int)volume, WAP, count));
        }

        public event AxTWSLib._DTwsEvents_fundamentalDataEventHandler fundamentalData;
        void EWrapper.fundamentalData(int reqId, string data)
        {
            if (fundamentalData != null) fundamentalData(this, new AxTWSLib._DTwsEvents_fundamentalDataEvent(reqId, data));
        }

        public event AxTWSLib._DTwsEvents_orderStatusEventHandler orderStatus;
        void EWrapper.orderStatus(int orderId, string status, int filled, int remaining, double avgFillPrice, int permId, int parentId, double lastFillPrice, int clientId, string whyHeld)
        {
            if (orderStatus != null) orderStatus(this, new AxTWSLib._DTwsEvents_orderStatusEvent(orderId, status, filled, remaining, avgFillPrice, permId, parentId, lastFillPrice, clientId, whyHeld));
        }

        public event AxTWSLib._DTwsEvents_currentTimeEventHandler currentTime;
        void EWrapper.currentTime(long time)
        {
            if (currentTime != null) currentTime(this, new AxTWSLib._DTwsEvents_currentTimeEvent((int)time));
        }

        public void accountSummary(int reqId, string account, string tag, string value, string currency)
        {
            logger.Debug("Not implemented {0}", MethodInfo.GetCurrentMethod().Name);
            throw new NotImplementedException();
        }

        public void accountSummaryEnd(int reqId)
        {
            logger.Debug("Not implemented {0}", MethodInfo.GetCurrentMethod().Name);
            throw new NotImplementedException();
        }

        public void commissionReport(CommissionReport commissionReport)
        {
            logger.Debug("Not implemented {0}", MethodInfo.GetCurrentMethod().Name);
            //throw new NotImplementedException();
        }

        public void deltaNeutralValidation(int reqId, UnderComp underComp)
        {
            logger.Debug("Not implemented {0}", MethodInfo.GetCurrentMethod().Name);
            throw new NotImplementedException();
        }

        public void marketDataType(int reqId, int marketDataType)
        {
            logger.Debug("Not implemented {0}", MethodInfo.GetCurrentMethod().Name);
            throw new NotImplementedException();
        }

        public void position(string account, Contract contract, int pos)
        {
            logger.Debug("Not implemented {0}", MethodInfo.GetCurrentMethod().Name);
            throw new NotImplementedException();
        }

        public void positionEnd()
        {
            logger.Debug("Not implemented {0}", MethodInfo.GetCurrentMethod().Name);
            throw new NotImplementedException();
        }

        public void receiveFA(int faDataType, string faXmlData)
        {
            logger.Debug("Not implemented {0}", MethodInfo.GetCurrentMethod().Name);
            throw new NotImplementedException();
        }

        public void scannerData(int reqId, int rank, IBApi.ContractDetails contractDetails, string distance, string benchmark, string projection, string legsStr)
        {
            logger.Debug("Not implemented {0}", MethodInfo.GetCurrentMethod().Name);
            throw new NotImplementedException();
        }

        public void scannerDataEnd(int reqId)
        {
            logger.Debug("Not implemented {0}", MethodInfo.GetCurrentMethod().Name);
            throw new NotImplementedException();
        }

        public void scannerParameters(string xml)
        {
            logger.Debug("Not implemented {0}", MethodInfo.GetCurrentMethod().Name);
            throw new NotImplementedException();
        }

        public void tickEFP(int tickerId, int tickType, double basisPoints, string formattedBasisPoints, double impliedFuture, int holdDays, string futureExpiry, double dividendImpact, double dividendsToExpiry)
        {
            logger.Debug("Not implemented {0}", MethodInfo.GetCurrentMethod().Name);
            throw new NotImplementedException();
        }

        public void tickOptionComputation(int tickerId, int field, double impliedVolatility, double delta, double optPrice, double pvDividend, double gamma, double vega, double theta, double undPrice)
        {
            logger.Debug("Not implemented {0}", MethodInfo.GetCurrentMethod().Name);
            throw new NotImplementedException();
        }

        public void tickSnapshotEnd(int tickerId)
        {
            logger.Debug("Not implemented {0}", MethodInfo.GetCurrentMethod().Name);
            throw new NotImplementedException();
        }
    }
}